package org.zoomdev.zoom.aop.utils;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.ioc.MethodParameterNamesGetter;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class AsmMethodParameterNamesGetter implements MethodParameterNamesGetter {

    @Override
    public String[] getParameterNames(Class<?> clazz, Method method) {
        try (InputStream in = getClassBytesStream(clazz)) {
            ClassReader reader = new ClassReader(in);
            ParameterNameCollector collector = new ParameterNameCollector(method);
            reader.accept(collector, ClassReader.SKIP_FRAMES);
            return collector.getNames();
        } catch (IOException e) {
            throw new ZoomException("解析字节码失败", e);
        }
    }

//    private InputStream getClassBytesStream(Class<?> clazz) {
//        String className = clazz.getName().replace('.', '/') + ".class";
//        return clazz.getClassLoader().getResourceAsStream(className);
//    }

    private InputStream getClassBytesStream(Class<?> clazz) {
        String className = clazz.getName().replace('.', '/') + ".class";
        ClassLoader classLoader = clazz.getClassLoader();
        InputStream inputStream;
        if (classLoader == null) {
            // 处理由启动类加载器加载的类
            inputStream = ClassLoader.getSystemResourceAsStream(className);
        } else {
            inputStream = classLoader.getResourceAsStream(className);
        }
        if (inputStream == null) {
            throw new ZoomException("无法找到类资源: " + className);
        }
        return inputStream;
    }

    private static class ParameterNameCollector extends ClassVisitor {
        private final Method targetMethod;
        private final List<String> paramNames = new ArrayList<>();
        private String currentMethodName;
        private String currentMethodDesc;

        public ParameterNameCollector(Method method) {
            super(Opcodes.ASM9);
            this.targetMethod = method;
            this.currentMethodName = method.getName();
            this.currentMethodDesc = Type.getMethodDescriptor(method);
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc,
                                         String signature, String[] exceptions) {
            if (name.equals(currentMethodName) && desc.equals(currentMethodDesc)) {
                return new MethodParamVisitor(api, paramNames,
                        Type.getArgumentTypes(currentMethodDesc).length,
                        (access & Opcodes.ACC_STATIC) != 0);
            }
            return null;
        }

        public String[] getNames() {
            return paramNames.toArray(new String[0]);
        }
    }

    private static class MethodParamVisitor extends MethodVisitor {
        private final List<String> paramNames;
        private final int paramCount;
        private final boolean isStatic;

        public MethodParamVisitor(int api, List<String> paramNames,
                                  int paramCount, boolean isStatic) {
            super(api);
            this.paramNames = paramNames;
            this.paramCount = paramCount;
            this.isStatic = isStatic;
        }

        @Override
        public void visitLocalVariable(String name, String descriptor,
                                       String signature, Label start,
                                       Label end, int index) {
            // 静态方法参数从0开始，实例方法从1开始（this指针）
            int offset = isStatic ? 0 : 1;

            if (index >= offset && index < paramCount + offset) {
                paramNames.add(name);
            }
        }
    }
}